<?php

namespace App\Factories;

use App\Enums\FontFamily;
use App\Enums\LayerType;
use App\Models\Design;
use App\Models\Player;
use Exception;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Collection;
use Intervention\Image\Image;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Laravel\Facades\Image as ImageManager;
use Intervention\Image\Typography\FontFactory;
use SimpleSoftwareIO\QrCode\Facades\QrCode;
use Vinkla\Hashids\Facades\Hashids;

class PosterFactory
{
    protected Image $image;

    protected Collection $designs;

    protected Design $background;

    protected int $canvasWidth = 750;

    protected Filesystem $storage;

    /**
     * @throws Exception
     */
    public function __construct(protected Player $player, protected bool $isPreview = false)
    {
        $background = $this->player->season->designs()
            ->where('type', LayerType::BACKGROUND)
            ->first();
        if (!$background) {
            throw new Exception('必须配置背景图片');
        }
        $this->designs = $this->player->season->designs()
            ->where('type', '<>', LayerType::BACKGROUND)
            ->ordered()
            ->get();

        $this->storage = $this->player->application->domainPool->storage();
        $this->background = $background;
    }

    private function init(): void
    {
        $bg = $this->storage->get($this->background->config['background']['cover']);

        $this->image = ImageManager::read($bg)
            ->scale($this->canvasWidth);

        foreach ($this->designs as $design) {
            if (method_exists($this, $design->type->value)) {
                call_user_func([$this, $design->type->value], $design);
            }
        }
    }

    public function resource(): EncodedImageInterface
    {
        $this->init();

        return $this->image->toJpeg();
    }

    /**
     * Notes   : html展示标签
     *
     * @Date   : 2024/8/5 上午10:56
     * @Author : <Jason.C>
     * @return string
     */
    public function show(): string
    {
        return "<img width='$this->canvasWidth' src='{$this->resource()->toDataUri()}'  alt=''/>";
    }

    /**
     * Notes   : 插入选手头像
     *
     * @Date   : 2023/11/30 11:55
     * @Author : <Jason.C>
     * @param  Design  $design
     */
    private function avatar(Design $design): void
    {
        $avatar = ImageManager::read($this->storage->get($this->player->cover));
        if ($design->config['avatar']['size']) {
            $avatar->scale($design->config['avatar']['size']);
        } else {
            $avatar->scale($this->canvasWidth / 2);
        }
        $this->insertToCanvas($avatar, $design);
    }

    /**
     * Notes   : 将资源插入画布
     *
     * @Date   : 2024/8/5 上午10:57
     * @Author : <Jason.C>
     * @param  mixed  $resource
     * @param  Design  $design
     */
    private function insertToCanvas(mixed $resource, Design $design): void
    {
        $this->image->place($resource, $design->position->value, $design->x_axis, $design->y_axis);
    }

    /**
     * Notes   : 插入自定义图层
     *
     * @Date   : 2023/11/30 11:55
     * @Author : <Jason.C>
     * @param  Design  $design
     */
    private function image(Design $design): void
    {
        $image = ImageManager::read($this->storage->get($design->config['image']['cover']));
        if ($design->config['image']['size']) {
            $image->scale($design->config['image']['size']);
        } else {
            $image->scale($this->canvasWidth);
        }
        $this->insertToCanvas($image, $design);
    }

    /**
     * Notes   : 插入选手姓名
     *
     * @Date   : 2024/8/5 上午10:58
     * @Author : <Jason.C>
     * @param  Design  $design
     */
    private function name(Design $design): void
    {
        $this->image->text(
            $this->player->name,
            $design->x_axis,
            $design->y_axis,
            function (FontFactory $font) use ($design) {
                $ff = FontFamily::tryFrom($design->config['name']['font_family']);
                $font->file($ff->toAsset());
                $font->size($design->config['name']['font_size']);
                $font->color($design->config['name']['font_color']);
                $font->stroke($design->config['name']['stroke_color'], $design->config['name']['stroke_width']);
                $font->align($design->config['name']['text_align']);
                $font->valign($design->config['name']['text_valign']);
                $font->angle($design->config['name']['text_angle']);
            }
        );
    }

    /**
     * Notes   : 自定义文字
     *
     * @Date   : 2024/8/5 上午10:58
     * @Author : <Jason.C>
     * @param  Design  $design
     */
    private function text(Design $design): void
    {
        $this->image->text(
            $design->config['text']['text'],
            $design->x_axis,
            $design->y_axis,
            function (FontFactory $font) use ($design) {
                $ff = FontFamily::tryFrom($design->config['text']['font_family']);
                $font->file($ff->toAsset());
                $font->size($design->config['text']['font_size']);
                $font->color($design->config['text']['font_color']);
                $font->stroke($design->config['text']['stroke_color'], $design->config['text']['stroke_width']);
                $font->align($design->config['text']['text_align']);
                $font->valign($design->config['text']['text_valign']);
                $font->angle($design->config['text']['text_angle']);
            }
        );
    }

    /**
     * Notes   : 插入二维码
     *
     * @Date   : 2024/8/5 上午10:58
     * @Author : <Jason.C>
     * @param  Design  $design
     * @throws Exception
     */
    private function qrcode(Design $design): void
    {
        if (!$this->isPreview) {
            $domain = $this->player->chapter->getMiddleDomain();
            if (!$domain) {
                throw new Exception('活动未配置活码域名');
            }

            $rand = rand(1, 999);
            # 通过hashid来生成二级域名
            $hash = Hashids::connection('slugs')->encode(
                $this->player->application_id,
                $this->player->season_id,
                $this->player->chapter_id,
                $this->player->id,
                $rand
            );

            $guide = $this->player->guides()->create([
                'application_id' => $this->player->application_id,
                'slug' => $hash,
                'rand' => $rand,
                'domain' => $domain->makeSubDomain($hash),
            ]);

            $qrCodeContent = $guide->domain.'?verify='.$rand;
        } else {
            $qrCodeContent = 'This is demo QrCode Content';
        }

        if ($design->config['qrcode']['size']) {
            $size = $design->config['qrcode']['size'];
        } else {
            $size = $this->canvasWidth / 3;
        }
        $qrcode = QrCode::format('png')
            ->margin($design->config['qrcode']['margin'])
            ->errorCorrection($design->config['qrcode']['correction'])
            ->size($size)
            ->backgroundColor(
                ...$this->hexToRgba($design->config['qrcode']['background'],
                $design->config['qrcode']['background_alpha'])
            )
            ->color(
                ...$this->hexToRgba($design->config['qrcode']['color'],
                $design->config['qrcode']['color_alpha'])
            )
            ->generate($qrCodeContent);

        $this->insertToCanvas($qrcode->toHtml(), $design);
    }

    /**
     * Notes   : 十六进制颜色转rgba
     *
     * @Date   : 2024/8/5 上午10:58
     * @Author : <Jason.C>
     * @param  string  $hex
     * @param  int  $alpha
     * @return array
     */
    private function hexToRgba(string $hex, int $alpha = 100): array
    {
        $hex = ltrim($hex, '#');
        $red = base_convert(substr($hex, 0, 2), 16, 10);
        $green = base_convert(substr($hex, 2, 2), 16, 10);
        $blue = base_convert(substr($hex, 4, 2), 16, 10);

        return [$red, $green, $blue, $alpha];
    }
}
